#include "CodeGenerator.h"

function initCodeGenerator()
{
    int i;
    for(i = 0; i < NOOFREG; i++)
    {
        registers[i] = 0;
    }
    for(i = 0; i < MAXCODE; i++)
    {
        code[i] = newCodeLine;
    }

    globalAddress = -1;
	programCounter = 0;
}

char ptr getInstruction(int key)
{
    if(MOV equ key)
        return "MOV";
    elif(MVN equ key)
        return "MVN";
    elif(ADD equ key)
        return "ADD";
    elif(SUB equ key)
        return "SUB";
    elif(MUL equ key)
        return "MUL";
    elif(Div equ key)
        return "Div";
    elif(Mod equ key)
        return "Mod";
    elif(CMP equ key)
        return "CMP";
    elif(MOVI equ key)
        return "MOVI";
    elif(MVNI equ key)
        return "MVNI";
    elif(ADDI equ key)
        return "ADDI";
    elif(SUBI equ key)
        return "SUBI";
    elif(MULI equ key)
        return "MULI";
    elif(DIVI equ key)
        return "DIVI";
    elif(MODI equ key)
        return "MODI";
    elif(CMPI equ key)
        return "CMPI";
    elif(CHKI equ key)
        return "CHKI";
    elif(LDW equ key)
        return "LDW";
    elif(LDB equ key)
        return "LDB";
    elif(POP equ key)
        return "POP";
    elif(STW equ key)
        return "STW";
    elif(STB equ key)
        return "STB";
    elif(PSH equ key)
        return "PSH";
    elif(RD equ key)
        return "RD";
    elif(WRD equ key)
        return "WRD";
    elif(WRH equ key)
        return "WRH";
    elif(WRC equ key)
        return "WRC";
    elif(WRL equ key)
        return "WRL";
    elif(BEQ equ key)
        return "BEQ";
    elif(BNE equ key)
        return "BNE ";
    elif(BLT equ key)
        return "BLT";
    elif(BGE equ key)
        return "BGE";
    elif(BLE equ key)
        return "BLE";
    elif(BGT equ key)
        return "BGT";
    elif(BR equ key)
        return "BR";
    elif(BL equ key)
        return "BL";
    elif(HLT equ key)
        return "HLT";
    printf("Unkn instruction!\n");

    return NULL;
}

function arithOperation(int operation, struct Item ptr result, struct Item ptr opItem)
{
    if(result-> iClass equ CLASSCONST and opItem-> iClass equ CLASSCONST)
    {
        if(operation equ PLUS)
        {
            result-> iAddress = result-> iAddress + opItem-> iAddress;
        }
        elif(operation equ MINUS)
        {
            result-> iAddress = result-> iAddress - opItem-> iAddress;
        }
        elif(operation equ TIMES)
        {
            result-> iAddress = result-> iAddress * opItem-> iAddress;
        }
        elif(operation equ DIV)
        {
            result-> iAddress = result-> iAddress / opItem-> iAddress;
        }
        elif(operation equ MOD)
        {
            result-> iAddress = result-> iAddress % opItem-> iAddress;
        }
        elif(operation equ EQL)
        {
            result-> iAddress = result-> iAddress equ opItem-> iAddress;
        }
        elif(operation equ NEQ)
        {
            result-> iAddress = result-> iAddress neq opItem-> iAddress;
        }
        elif(operation equ LSS)
        {
            result-> iAddress = result-> iAddress < opItem-> iAddress;
        }
        elif(operation equ GRT)
        {
            result-> iAddress = result-> iAddress > opItem-> iAddress;
        }
        elif(operation equ LEQ)
        {
            result-> iAddress = result-> iAddress <= opItem-> iAddress;
        }
        elif(operation equ GEQ)
        {
            result-> iAddress = result-> iAddress >= opItem-> iAddress;
        }
        else
        {
            mark("Operator unknown", 1);
        }
    }
    else
    {
        if(operation equ PLUS)
        {
            put(ADD, result, opItem);
        }
        elif(operation equ MINUS)
        {
            put(SUB, result, opItem);
        }
        elif(operation equ TIMES)
        {
            put(MUL, result, opItem);
        }
        elif(operation equ DIV)
        {
            put(DIV, result, opItem);
        }
        elif(operation equ MOD)
        {
            put(Mod, result, opItem);
        }
        elif(operation equ EQL)
        {
            put(CMP, result, opItem);
        }
        elif(operation equ NEQ)
        {
            put(CMP, result, opItem);
        }
        elif(operation equ LSS)
        {
            put(CMP, result, opItem);
        }
        elif(operation equ GRT)
        {
            put(CMP, result, opItem);
        }
        elif(operation equ LEQ)
        {
            put(CMP, result, opItem);
        }
        elif(operation equ GEQ)
        {
            put(CMP, result, opItem);
        }
        else
        {
            mark("Operator unknown", 1);
        }
    }
}

function put(int operation, struct Item ptr i1, struct Item ptr i2)
{
    loadVar(i1);

    if(i2-> iClass equ CLASSCONST)
    {
        if(operation equ ADD)
        {
            addCode(ADDI, i1-> iRegister, i1-> iRegister, i2-> iRegister);
        }
        elif(operation equ SUB)
        {
            addCode(SUBI, i1-> iRegister, i1-> iRegister, i2-> iRegister);
        }
        elif(operation equ MUL)
        {
            addCode(MULI, i1-> iRegister, i1-> iRegister, i2-> iRegister);
        }
        elif(operation equ Div)
        {
            addCode(DIVI, i1-> iRegister, i1-> iRegister, i2-> iRegister);
        }
        elif(operation equ Mod)
        {
            addCode(MODI, i1-> iRegister, i1-> iRegister, i2-> iRegister);
        }
        else
        {
            mark("unknown operation", 1);
        }

    }
    else
    {
        if(i2-> iClass neq CLASSREG)
        {
            loadVar(i2);
        }
        addCode(operation, i1-> iRegister, i1-> iRegister, i2-> iRegister);
        freeReg(i2-> iRegister);
    }
}

function addCode(int operation, int reg1, int reg2, int reg3)
{
    code[programCounter]-> instruction = operation;
    code[programCounter]-> reg1 = reg1;
    code[programCounter]-> reg2 = reg2;
    code[programCounter]-> reg3 = reg3;
    programCounter = programCounter + 1;
}

int getRegister()
{
    int index = 1;
    while(index < NOOFREG and registers[index] neq 0)
    {
        index = index + 1;
    }
    if(index == NOOFREG)
    {
        mark("All Registers in use", 1);
    }
    else
    {
        registers[index] = 1;
    }
    return index;
}

function loadVar(struct Item ptr item)
{
    int reg = 0;
    if(item-> iClass equ CLASSVAR)
    {
        reg = getRegister();
        addCode(LDW, reg, item-> iRegister, item-> iAddress);
        freeReg(item-> iRegister);
        item-> iRegister = reg;
    }
    elif(item-> iClass equ CLASSCONST)
    {
        item-> iRegister = getRegister();
        addCode(ADDI, item-> iRegister, 0, item-> iAddress);
    }
    item-> iClass = CLASSREG;
}

function freeReg(int reg)
{
    if(reg < 0)
    {
        mark("register negativ", 1);
    }
	elif(reg > NOOFREG)
	{
		mark("register too high", 1);
	}
	else
	{
		registers[reg] = 0;
	}
}

struct Item ptr makeItemFromObject(struct Object ptr o)
{
    struct Item ptr item = newItem;
    item-> iClass = o-> oClass;
    item-> iType = o-> type;
    item-> iAddress = o-> val;

    item-> iRegister = programCounter;

    return item;
}

function calculateAddresses()
{
    int size = 0;
    while(hasNextString())
    {
        struct Object ptr cur = search(getNextString());
        if(cur-> type-> form neq FORMARRAY)
        {
            globalAddress = globalAddress - (cur-> type-> len);
            cur-> val = globalAddress;
        }
    }
    resetStringListPtr();
}

function run()
{
    FILE ptr outPutFile = fopen ("outPut.eas", "w");
	if(outPutFile equ NULL)
	{
	    printf ("Can not open file: outPut.eas\n");
		exit(1);
	}
	printf("\n\n");
    int i = 0;
    while(i < programCounter)
    {
        printf(             "%s %d %d %d\n", getInstruction(code[i]-> instruction), code[i]-> reg1, code[i]-> reg2, code[i]-> reg3);
        fprintf(outPutFile, "%s %d %d %d\n", getInstruction(code[i]-> instruction), code[i]-> reg1, code[i]-> reg2, code[i]-> reg3);
        i++;
    }
    printf("\n\n");
    fclose(outPutFile);
}

function printItem(struct Item ptr item)
{
    if(item neq NULL)
    {
        printf("iClass: %d\n", item-> iClass);
        printType(item-> iType);
        printf("iAddress: %d\n", item-> iAddress);
        printf("iWrongAddress: %d\n", item-> iWrongAddress);
        printf("iBooleanOperation: %d\n", item-> iBooleanOperation);
        printf("iRegister: %d\n\n", item-> iRegister);
    }
    else
    {
        mark("item is NULL", 1);
    }
}
